


#ifndef __QE_SHELL_H__
#define __QE_SHELL_H__



#include "qe_def.h"
#include "qe_buffer.h"
#include "autoconfig.h"



#define QE_SHELL_DEFAULT_USER       CONFIG_SHELL_DEFAULT_USER
#define QE_SHELL_DEFAULT_PSWD       CONFIG_SHELL_DEFAULT_PSWD

#define QE_SHELL_KEY_CODE_UP        0x1B5B4100
#define QE_SHELL_KEY_CODE_DOWN      0x1B5B4200
#define QE_SHELL_KEY_CODE_LEFT      0x1B5B4400
#define QE_SHELL_KEY_CODE_RIGHT     0x1B5B4300
#define QE_SHELL_KEY_CODE_ENTER     0x0D000000
#define QE_SHELL_KEY_CODE_BACKSPACE 0x08000000


/*
 * Command Attribute
 * =================
 */
#define QE_SHELL_CMD_ATTR_ENABLE_UNCHECKED     12
#define QE_SHELL_CMD_ATTR_ENABLE_RETURN        13
#define QE_SHELL_CMD_ATTR_READ_ONLY            14

#define QE_SHELL_CMD_PERM(perm)    (perm & 0x000000FF)
#define QE_SHELL_CMD_TYPE(type)    ((type & 0x0000000F) << 8)
#define QE_SHELL_CMD_EUCK          (1 << QE_SHELL_CMD_ATTR_ENABLE_UNCHECKED)
#define QE_SHELL_CMD_ERET          (1 << QE_SHELL_CMD_ATTR_ENABLE_RETURN)
#define QE_SHELL_CMD_RDOY          (1 << QE_SHELL_CMD_ATTR_READ_ONLY)
#define QE_SHELL_CMD_PNUM(num)     ((num & 0x0000000F) << 16)


/*
 * Command Declaration
 * ===================
 */
#ifndef CONFIG_SHELL_USING_EXPORT
    /**
     * @brief shell command item
     * 
     * @param _attr command attribute
     * @param _name command name
     * @param _func command function
     * @param _desc command description
     */
    #define QE_SHELL_CMD_ITEM(_attr, _name, _func, _desc) \
        {\
            .attr.value = _attr, \
            .data.cmd.name = #_name,\
            .data.cmd.function = (int (*)())_func,\
            .data.cmd.desc = #_desc\
        }

    /**
     * @brief shell function item
     * 
     * @param _pnum function parameters number
     * @param _name function name
     * @param _func function function
     * @param _desc function description
     */
    #define QE_SHELL_CMD_FUNC(_pnum, _name, _func, _desc) \
        QE_SHELL_CMD_ITEM(QE_SHELL_CMD_PNUM(_pnum)|QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_CMD_FUNC), \
            _name, _func, _desc)

    /**
     * @brief shell execute item
     * 
     * @param _name execute name
     * @param _func execute function
     * @param _desc execute description
     */
    #define QE_SHELL_CMD_EXEC(_name, _func, _desc) \
        QE_SHELL_CMD_ITEM(QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_CMD_MAIN), \
            _name, _func, _desc)

    /**
     * @brief shell variable item
     * 
     * @param _attr  variable attribute
     * @param _name  variable name
     * @param _value variable value
     * @param _desc  variable description
     */
    #define QE_SHELL_VAR_ITEM(_attr, _name, _value, _desc) \
        { \
            .attr.value = _attr, \
            .data.var.name = #_name, \
            .data.var.value = (void *)_value, \
            .data.var.desc = #_desc \
        }

    /**
     * @brief shell int variable item
     * 
     * @param _name  variable name
     * @param _value variable value
     * @param _desc  variable description
     */
    #define QE_SHELL_VAR_INT(_name, _value, _desc) \
        QE_SHELL_VAR_ITEM(QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_VAR_INT), _name, _value, _desc)

    /**
     * @brief shell string variable item
     * 
     * @param _name  variable name
     * @param _value variable value
     * @param _desc  variable description
     */
    #define QE_SHELL_VAR_STR(_name, _value, _desc) \
        QE_SHELL_VAR_ITEM(QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_VAR_STRING), _name, _value, _desc)

    /**
     * @brief shell user item
     * 
     * @param _attr user attribute
     * @param _name user name
     * @param _pswd user password
     * @param _desc user description
     */
    #define QE_SHELL_USR_ITEM(_name, _pswd, _desc) \
        {\
            .attr.value = QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_USR), \
            .data.usr.name = _name, \
            .data.usr.password = _pswd, \
            .data.usr.desc = #_desc \
        }

    /**
     * @brief shell key item
     * 
     * @param _value user name
     * @param _func user password
     * @param _desc user description
     */
    #define QE_SHELL_KEY_ITEM(_value, _func, _desc) \
        {\
            .attr.value = QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_KEY), \
            .data.key.value = _value, \
            .data.key.function = (void (*)(qe_shell *))_func, \
            .data.key.desc = #_desc \
        }
#else

    /**
     * @brief shell command export
     * 
     * @param _name command name
     * @param _func command function
     * @param _desc command description
     */
    #define QE_SHELL_EXPORT_CMD(_attr, _name, _func, _desc) \
        const char qe_shell_cmd_nstr_##_name[] = #_name;\
        const char qe_shell_cmd_desc_##_name[] = #_desc;\
        qe_used const qe_shell_command \
        qe_shellcmd_##_name qe_section(".qe_shellcmd.1") = \
        {\
            .attr.value = _attr, \
            .data.cmd.name = qe_shell_cmd_nstr_##_name,\
            .data.cmd.function = (int (*)())_func, \
            .data.cmd.desc = qe_shell_cmd_desc_##_name\
        }

    #define QE_SHELL_EXPORT_CMD_FUNC(_pnum, _name, _func, _desc) \
        QE_SHELL_EXPORT_CMD(QE_SHELL_CMD_PNUM(_pnum)|QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_CMD_FUNC), _name, _func, _desc)

    #define QE_SHELL_EXPORT_CMD_EXEC(_name, _func, _desc) \
        QE_SHELL_EXPORT_CMD(QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_CMD_MAIN), _name, _func, _desc)
 

    #define QE_SHELL_EXPORT_VAR(_attr, _name, _value, _desc)\
        const char qe_shell_var_name_##_name[] = #_name;\
        const char qe_shell_var_desc_##_name[] = #_desc;\
        qe_used const qe_shell_command \
        qe_shellvar_##_name qe_section(".qe_shellcmd.1") = \
        {\
            .attr.value = _attr, \
            .data.var.name = qe_shell_var_name_##_name,\
            .data.var.value = (void *)_value, \
            .data.var.desc = qe_shell_var_desc_##_name \
        }

    #define QE_SHELL_EXPORT_VAR_INT(_name, _value, _desc)\
        QE_SHELL_EXPORT_VAR(QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_VAR_INT), _name, _value, _desc)

    #define QE_SHELL_EXPORT_VAR_STR(_name, _value, _desc)\
        QE_SHELL_EXPORT_VAR(QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_VAR_STRING), _name, _value, _desc)

    #define QE_SHELL_EXPORT_USR(_name, _pswd, _desc)\
        const char qe_shell_usr_name_##_name[] = _name;\
        const char qe_shell_usr_pswd_##_name[] = _pswd;\
        const char qe_shell_usr_desc_##_name[] = #_desc;\
        qe_used const qe_shell_command \
        qe_shellusr_##_name qe_section(".qe_shellcmd.1") = \
        {\
            .attr.value = QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_USR),\
            .data.usr.name = qe_shell_usr_name_##_name,\
            .data.usr.password = qe_shell_usr_pswd_##_name,\
            .data.usr.desc = qe_shell_usr_desc_##_name\
        }

    #define QE_SHELL_EXPORT_START(_name)\
        const char qe_shell_cmd_name_##_name[] = #_name;\
        const char qe_shell_cmd_desc_##_name[] = #_name;\
        qe_used const qe_shell_command \
        qe_shellcmd_##_name qe_section(".qe_shellcmd.0") = \
        {\
            .attr.value = QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_CMD_NONE),\
        }

    #define QE_SHELL_EXPORT_END(_name)\
        const char qe_shell_cmd_name_##_name[] = #_name;\
        const char qe_shell_cmd_desc_##_name[] = #_name;\
        qe_used const qe_shell_command \
        qe_shellcmd_##_name qe_section(".qe_shellcmd.2.end") = \
        {\
            .attr.value = QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_CMD_NONE),\
        }

    #define QE_SHELL_EXPORT_KEY(_value, _func, _desc)\
        const char qe_shell_key_desc_##_value[] = #_desc;\
        qe_used const qe_shell_command \
        qe_shellkey_##_value qe_section(".qe_shellcmd.1") = \
        {\
            .attr.value = QE_SHELL_CMD_TYPE(QE_SHELL_TYPE_KEY),\
            .data.key.value = _value,\
            .data.key.function = (void (*)(qe_shell *))_func, \
            .data.key.desc = qe_shell_key_desc_##_value\
        }

#define QE_SHELLCMD_FORCE_EXPORT(_name) \
    void _name##_shellcmd_force_include(void) { \
        (void)&qe_shellcmd_##_name; \
    };

#define QE_SHELLCMD_FORCE_IMPORT(_name) \
    extern void _name##_shellcmd_force_include(void); \
    _name##_shellcmd_force_include();

#endif


/*
 * Command Type
 * ============
 */
typedef enum {
    QE_SHELL_TYPE_CMD_NONE = 0,
    QE_SHELL_TYPE_CMD_MAIN,
    QE_SHELL_TYPE_CMD_FUNC,
    QE_SHELL_TYPE_VAR_INT,
    QE_SHELL_TYPE_VAR_SHORT,
    QE_SHELL_TYPE_VAR_CHAR,
    QE_SHELL_TYPE_VAR_STRING,
    QE_SHELL_TYPE_VAR_POINT,
    QE_SHELL_TYPE_VAR_NODE,
    QE_SHELL_TYPE_USR,
    QE_SHELL_TYPE_KEY,
} qe_shell_cmdtype_e;


/*
 * Number Type
 * ===========
 */
typedef enum
{
    QE_SHELL_NUM_DEC,
    QE_SHELL_NUM_BIN,
    QE_SHELL_NUM_OCT,
    QE_SHELL_NUM_HEX,
    QE_SHELL_NUM_FLOAT
} qe_shell_numtype_e;

typedef struct {
    void *var;          /* variable reference */
    int (*get)();       /* variable get method */
    int (*set)();       /* variable set method */
} qe_shell_node_attr;

/* 
 * Shell and Command Struct
 * ==============
 */

struct qe_shell_ctx;
typedef struct qe_shell_ctx qe_shell;

typedef int (*qe_shell_io)(qe_shell *, char *, int);

typedef struct qe_shell_ctx {

    struct {
        const struct qe_shell_command *user;
        int active_time;
        char *path;
    } info;

    struct {
        int key;
        int cursor;
        int length;
        int nr_params;
        int bufsz;
        qe_gbuf *buf;
        char *param[CONFIG_SHELL_PARAM_MAX_NUM];
    } parser;

#if CONFIG_SHELL_HISTORY_MAX_NUM > 0
    struct {
        char *item[CONFIG_SHELL_HISTORY_MAX_NUM];  /**< history item */
        int number;                         /**< history record number */
        int record;                         /**< current record location */
        int offset;                         /**< current record offset */
    } history;
#endif

    struct {
        qe_u32 initialized:1;
        qe_u32 checked:1;
        qe_u32 active:1;
        qe_u32 tab:1;
    } status;

    struct {
        void *base;
        int count;
    } cmdtab;

#if defined(CONFIG_SHELL_SHOW_USR_INFO)
    const char *usr_info;
#endif

    qe_shell_io read;
    qe_shell_io write;

    qe_ptr priv;

} qe_shell;

typedef struct qe_shell_command {
    
    union {
        struct {
            qe_u8 permission:8;         /* command permission */
            qe_u8 type:4;               /* command type */
            qe_u8 enable_unckecked:1;   /* command exec with password unchecked */
            qe_u8 enable_return:1;      /* command exec print return value */
            qe_u8 read_only:1;          /* command read only */
            qe_u8 reserve:1;            
            qe_u8 num_params:4;         /* command parameters number */
        } attrs;
        qe_u32 value;
    } attr;

    union {
        struct {
            const char *name;       /* command name */
            int (*function)();      /* command call function */
            const char *desc;       /* command description */
        } cmd;
        struct {
            const char *name;       /* variable name */
            void *value;            /* variable value pointer */
            const char *desc;       /* variable description */
        } var;
        struct
        {
            const char *name;                                   /**< 用户名 */
            const char *password;                               /**< 用户密码 */
            const char *desc;                                   /**< 用户描述 */
        } usr;
        struct {
            int value;                      /* key value */
            void (*function)(qe_shell *);    /* key call function */
            const char *desc;               /* key call description */
        } key;
    } data;
} qe_shell_command;



/*
 * Public Function
 * ===============
 */

qe_ret qe_shell_init(qe_shell *sh, qe_size bufsz, 
#ifndef CONFIG_SHELL_USING_EXPORT
    void *tab, int tabsize, 
#endif
    const char *user, qe_shell_io read, qe_shell_io write);
void qe_shell_handler(qe_shell *sh, char data);
void qe_shell_task(qe_shell *sh);
void qe_shell_key_up(qe_shell *sh);
void qe_shell_key_down(qe_shell *sh);
void qe_shell_key_left(qe_shell *sh);
void qe_shell_key_right(qe_shell *sh);
void qe_shell_key_enter(qe_shell *sh);
void qe_shell_key_backspace(qe_shell *sh);
void qe_shell_cmd_help(int argc, char *argv[]);
int qe_shell_cmd_setvar(char *name, int value);
int qe_shell_cmd_getvar(char *name);

#if CONFIG_SHELL_SUPPORT_END_LINE
void qe_shell_write_end_line(qe_shell *sh, char *buf, int len);
#endif

#if defined(CONFIG_SHELL_SHOW_USR_INFO)
qe_ret qe_shell_set_usr_info(qe_shell *sh, qe_const_str usr_info);
#endif

void qe_shell_set_priv(qe_shell *sh, qe_ptr priv);
void *qe_shell_get_priv(qe_shell *sh);

#endif /* __QE_SHELL_H__ */